/*async的流程控制*/
const async = require('async');
const fs = require('fs');
const util = require('util');
 
//测试用例函数
function showName(person, callback) {
    if ('name' in person) {
        //等待5秒以后开始执行操作，仍然是异步
        var id = setTimeout(function () {
            console.log('person_name:' + person.name);
            if (callback) {
                callback();
            }
        }, 5000);
        console.log('异步执行开始启动：等待5s, id=' + id);
    }
}
 
function showAge(person, callback) {
    if ('age' in person) {
        console.log('person_age:' + person.age);
        if (callback) {
            callback();
        }
    }
}
 
//异步流程控制测试类
function AsyncControlFlowTest() {
 
}
 
AsyncControlFlowTest.prototype = {
    //根据列出的函数来自动计算依赖关系，并根据依赖关系来执行异步操作
    //包含完整的数据流转，可以控制数据在每个处理过程中的结果保留
    //参数说明 auto({fnname1:fn1, fnname2:fn2, fnname3:[fn1, fn2, fn3],...}, callback)
    //fn之间可以具备依赖关系
    //fn的原型 (callback)，调用callback(err, result)，数据都以函数名称作为键值来保存数据结果
    //callback(err, reuslt)，数据结果
    auto: function () {
        //async.auto({
        //    // this function will just be passed a callback
        //    readData: async.apply(fs.readFile, 'data.txt', 'utf-8'),
        //    showData: ['readData', function(results, cb) {
        //        // results.readData is the file's contents
        //        // ...
        //    }]
        //}, callback);
        async.auto({
            get_data: function (callback) {
                //这种会阻塞整个程序，这样不行
                //for(var i=0; i<10000000000; ++i);
 
                console.log('in get_data');
                // async code to get some data
                callback(null, 'data', 'converted to array');
            },
            make_folder: function (callback) {
                console.log('in make_folder');
                // async code to create a directory to store a file in
                // this is run at the same time as getting the data
                callback(null, 'folder');
            },
            write_file: ['get_data', 'make_folder', function (results, callback) {
                //console.log('results:' + util.inspect(results,true));
                console.log('in write_file', JSON.stringify(results));
                // once there is some data and the directory exists,
                // write the data to a file in the directory
                callback(null, 'filename');
            }],
            email_link: ['write_file', function (results, callback) {
                console.log('in email_link', JSON.stringify(results));
                // once the file is written let's email a link to it...
                // results.write_file contains the filename returned by write_file.
                callback(null, {'file': results.write_file, 'email': 'user@example.com'});
            }]
        }, function (err, results) {
            console.log('err = ', err);
            console.log('results = ', results);
        });
    },
 
    //根据依赖项来计算依赖关系，依赖关系以回调函数参数的形式来进行表示
    //参数说明 autoInject({fnname1:fn1, fnname2:fn2,[fn1, fn2, fnname3,fn3],...}, callback)
    //fn之间可以设定各种依赖关系
    //fn1的原型 (callback)，调用callback的方式 callback(err, result1, result2, result3)
    //最后输出结果在result.fnname1中，以Array的形式来表现
    //callback (err, result) result包含了当前整个管道中所有的结果数据，执行完成auto调用该函数
    autoInject: function () {
        //  The example from `auto` can be rewritten as follows:
        //注入模式直接将依赖关系的函数结果作为了输入项
        async.autoInject({
            get_data: function (callback) {
                // async code to get some data
                callback(null, 'data', 'converted to array');
            },
            make_folder: function (callback) {
                // async code to create a directory to store a file in
                // this is run at the same time as getting the data
                callback(null, 'folder');
            },
            //这种注入方式必须要将参数的名称写正确了，参数的名称需要和函数的名称保持一致，
            //这样通过arguments就可以解析出函数的名称，就可以自动的计算出依赖关系了
            write_file: function (get_data, make_folder, callback) {
                // once there is some data and the directory exists,
                // write the data to a file in the directory
                console.log('arguments=' + console.log(util.inspect(arguments.callee.arguments, true)));
                console.log(util.inspect(get_data, true));
                console.log(util.inspect(make_folder, true));
                callback(null, 'filename');
            },
            email_link: function (write_file, callback) {
                // once the file is written let's email a link to it...
                // write_file contains the filename returned by write_file.
                callback(null, {'file': write_file, 'email': 'user@example.com'});
            }
        }, function (err, results) {
            console.log('err = ', err);
            console.log('email_link = ', results.email_link);
        });
    },
 
    //以参数的形式来计算依赖关系的另外一种表现形式
    //参数说明 autoInject({fnname1:fn1, fnname2:fn2,[fn1, fn2, fnname3,fn3],...}, callback)
    //fn之间可以设定各种依赖关系
    //fn1的原型 (callback)，调用callback的方式 callback(err, result1, result2, result3)
    //最后输出结果在result.fnname1中，以Array的形式来表现
    //callback (err, result) result包含了当前整个管道中所有的结果数据，执行完成auto调用该函数
    autoInject2: function () {
        //callback回调函数callback(err, result)，多个result会以数组的形式来提供
        async.autoInject({
            //...
            get_data: function (callback) {
                callback(null, 'data', 'convert to array');
            },
            make_folder: function (callback) {
                callback(null, 'make_folder');
            },
            //另外一种书写格式，这样写，回调函数的参数就不再是依赖项了，数组前面的最几个数据
            //描述的是依赖的函数，最后一个回调函数会将前几个依赖项的结果作为输入
            //怎么感觉很有管道的味道哦!!!!
            write_file: ['get_data', 'make_folder', function (get_data_arg, make_folder_arg, callback) {
                console.log('get_data:' + util.inspect(get_data_arg, true));
                console.log('make_folder' + util.inspect(make_folder_arg, true));
 
                callback(null, 'filename');
            }],
            email_link: ['write_file', function (write_file, callback) {
                callback(null, {'file': write_file, 'email': 'user@example.com'});
            }]
            //...
        }, function (err, results) {
            console.log('err = ', err);
            console.log('email_link = ', results.email_link);
        });
    },
 
    //货物处理，这个方法和queue的区别在于，cargo只有一个工作函数，该工作函数可以一次传入多个工作对象
    //而queue是会启动多个工作函数，多个工作函数同时异步工作
    //参数说明 cargo(function(task, callback), taskNum)
    //taskNum是异步函数一次能处理的数据量
    //task是一个数组
    cargo: function () {
        cargoObj = async.cargo(function (task, callback) {
            for (var i = 0; i < task.length; ++i) {
                console.log('当前的任务:' + task[i].name);
            }
 
            for (var i = 0; i < 1000000000; ++i);
            callback();
        }, 3);
 
        for (var i = 0; i < 7; ++i) {
            cargoObj.push({name: 'target' + i});
        }
    },
 
    //把一个输入数据，应用给每一个函数去分别异步并发执行
    //参数说明 applyEach(collection, input, callback)
    //collection是一个集合，用来存储所有要分别执行的函数，每个函数都是 (input, callback)，需要在函数中调用callback
    //input 是输入数据
    //callback 是所有的事情都完成以后会调用callback，无任何参数()
    applyEach: function () {
        async.applyEach([showName, showAge,], {name: 'async', age: '18'}, function () {
            console.log('完成所有的异步操作');
        });
    },
 
    //把一个输入数据，应用给每一个函数去分别依次去执行
    //参数说明 applyEachSeries(collection, input, callback)
    //collection 是一个函数集合，每个函数都是 (input, callback)，在函数中需要调用callback(作业完成)
    //input 是给每个函数的输入数据
    //callback 等所有的操作完成之后，会调用该函数，无任何参数 ()
    applyEachSeries: function () {
        async.applyEachSeries([showName, showAge,], {name: 'async2', age: '19'}, function () {
            console.log('逐个完成所有的异步操作');
        });
    },
 
    //compose会将每个操作一层层的包封组合起来，但是compose并不是同步执行
    //仍然是异步的，有可能外部包封的会先执行，这样可能会跟预想的不一样
    //参数说明: compose(fn1, fn2,...)
    //fn1 (result,callback)，输入数据流，以及回调函数callback(error, result)
    compose: function () {
        function add1(n, callback) {
            setTimeout(function () {
                callback(null, n + 1);
            }, 3000);
        };
 
        function mul3(n, callback) {
            setTimeout(function () {
                callback(null, n * 3);
            }, 2000)
        }
 
        var add1mul3 = async.compose(add1, mul3);
        add1mul3(20, function (err, result) {
            console.log('计算完成，结果=' + result);
        });
 
        //分别执行
        //async.applyEachSeries([add1, mul3], 20, function(err, result){
        //    console.log('applyEach计算完成，结果=' + result);
        //});
 
        //这个mul3，必须会在add1之后执行
        async.auto({
            add1: add1.bind(null,30),
            mul3: ['add1', function (results, callback) {
                var last = results.add1;
                mul3(last, callback);
            }],
        }, function (err, result) {
            console.log(err);
            console.log('auto计算的结果:' + result.mul3);
        })
 
    },
 
    //相当于java或c++的while语句
    //参数说明:during有三个参数 during(test, fn, callback)
    //test (callback)，调用callback(err, test_statement)
    //fn (callback)，调用callback()，跳转到条件函数中进行检测
    //callback (err)，如果err不是null，说明执行过程出错，否则说明跳出循环，执行完成
    during: function () {
        var count = 0;
        async.during(function (callback) {
                console.log('验证是否满足during的跳出条件...');
                callback(null, count < 5);
            },
            function (callback) {
                setTimeout(function () {
                    count++;
                    callback();
                }, 1000);
            },
            function (err) {
                console.log('已经异步过去了5秒钟');
            });
    },
}
 
var aft = new AsyncControlFlowTest();
var filter_member = ['auto']
for (var m in  aft) {
    if (typeof(aft[m]) == 'function' && filter_member.indexOf(m) == -1) {
        console.log('开始测试:' + m);
        aft[m]();
    }
}